查看原文
其他

HBase原理与实践 | 生产环境上线前真的优化过吗?

The following article is from DataFlow范式 Author Jason

笔者今天给大家讲一下 HBase 生产环境中的实践,包括资源隔离、参数配置、性能优化等方面,部分内容参考《HBase原理与实践》(非常建议大家好好读一读,一定会大有收获),以及笔者的实战经验。

HBase 业务资源隔离

1. 读写分离场景

RegionServer 默认情况下只提供一个请求队列给所有业务使用,导致部分延迟较高的请求影响其他对延迟敏感的业务。针对这种情况,HBase 提供了读写队列隔离方案。

我们知道,HBase 有三种典型的API操作类型,分别为 get、scan 和write,其中 get 和 scan 属于 read 类型。默认场景下,HBase 只提供一个队列,所有请求都会进入该队列进行优先级排序。在一些场景下,我们要求这三种类型的访问尽可能的互相不影响,那么就需要在线上配置读写分离。

HBase 中的相关配置如下:

  1. hbase.ipc.server.callqueue.read.ratio

    如果将 hbase.ipc.server.callqueue.read.ratio 设置为0.5,则表示有50%的线程数处理读请求,剩余50%用于接收写请求。

  2. hbase.ipc.server.callqueue.scan.ratio

    如果将 hbase.ipc.server.callqueue.scan.ratio 设置为0.5,则代表在50%的读线程之中,再有50%的线程处理 scan,也就是全部线程的25%。

2. CPU/内存资源隔离

针对系统资源方面,HBase 提供了 RegionServer Group(RSGroup)方案。RSGroup 方案在HBase 1.4 以上版本才原生支持,但是之前 1.x 版本可以打社区 patch。不过如果使用 CDH 搭建的平台,比如 CDH 5.13.3,HBase 1.2 版本即可支持。

为了支持 RSGroup 功能,需要配置如下参数:

  1. <property>

  2. <name>hbase.coprocessor.master.classes</name>

  3. <value>org.apache.hadoop.hbase.rsgroup.RSGroupAdminEndpoint</value>

  4. </property>


  5. <property>

  6. <name>hbase.master.loadbalancer.class</name>

  7. <value>org.apache.hadoop.hbase.rsgroup.RSGroupBasedLoadBalancer</value>

  8. </property>

生产线上,分组原则一般遵循:

  • 在线业务和离线业务尽量划分到不同组

  • 重要业务和边缘业务尽量划分到不同组

  • 非常重要的业务尽量单独划分到独立组或者单独部署集群

使用RSGroup会自动关闭全局自动负载均衡,后续需要手工触发。

另外要隔离系统表,要么定义一个 system rsgroup 用来存储所有系统表,要么把系统表都默认放在 default rsgroup 中。所有其他用户的表都定义在非 default 的 rsgroups 中。

根据实践证明,笔者建议,针对 Dead 或有问题的节点,定义一个特殊的 rsgroup 是非常有用的。所有放在这个特殊的 rsgroup 中的节点是无法运行的,直到修复完成。

最后,使用 RSGroup 要注意几点:

  1. (1) 迁移完表后记得做 major compact(本地化) 

  2. hbase(main):001:0> major_compact 'tablename'

  3. (2) 迁移完表后做 hbase hbck 校验,防止添加迁移表的时候有部分 region 未上线的情况

  4. (3) 删除 group 之前需要将 group 下的 regionserver 和 table 都移除掉

  5. (4) default 组和其他的 rsgroup 不一样,default 是动态的,其他的 group 则是静态的

HBase HBCK

线上环境,需要定期地检测和监控 HBase 集群中 Region 的一致性和完整性,同时可以对损坏的集群进行修复。

HBase HBCK 主要工作在两种模式下:

  • 一致性检测只读模式

  • 多阶段修复模式

HBase 集群一致性状态

  • HBase Region 一致性

    1. 集群中所有 region 都被 assign,而且被分配到唯一一个 RegionServer 节点上

    2. 该 region 的状态在内存中、hbase:meta 表中以及 zookeeper 这三个地方需要保持一致

  • HBase 表的完整性

    对于集群中任意一张表,每个 rowkey 都仅能存在于一个 region 区间

最好制定一个周期计划,例行地执行 hbck 命令,并在多次出现不一致的情况下发出报警信息,通知运维人员定位分析,及时解决问题。

  1. hbase hbck

  2. hbase hbck -details

  3. # 如果集群规模大,region很多,hbck耗时很长,可以针对某些表执行:

  4. hbase hbck TableFoo TableBar

HBCK 的局部低危修复

低风险的 Region 一致性问题修复是指:

修复仅仅涉及 HBase Master 内存中 Region 状态、ZooKeeper 临时节点中 Region 状态以及 hbase:meta 元数据表中 Region 状态的修改、并不实际修改任何 HDFS 文件。

Region一致性问题修复有两个常用选项:

  • -fixAssignments 修复 assign 相关问题,如没有 assign、assign 不正确或者同时 assign 到多个 RegionServer 节点问题的 regions。

  • -fixMeta 主要修复 .regioninfo 文件和 hbase:meta 元数据表的不一致。修复的原则是以 HDFS 文件为准:如果 region 在 HDFS 上存在,但在 hbase.meta 表中不存在,就会在 hbase:meta 表中添加一条记录。反之如果在 HDFS 上不存在,而在 hbase:meta 表中存在,就会将 hbase:meta 表中对应的记录删除。

比如针对 HDFS Region 空洞(HDFS Region Holes)问题,解决方案可以添加 -fixHdfsHoles 选项进行修复,这个命令会在空洞形成的地方填充一个空 Region 。这个命令通常不单独使用,而是和 -fixMeta、-fixAssignments 一起使用,如下命令组合:

  1. hbase hbck -fixAssignments -fixMeta -fixHdfsHoles

或者使用选项 -repairHoles,等价于上面的命令:

  1. hbase hbck -repairHoles

HBCK高危修复

Region 区间 overlap 相关问题的修复属于高危修复操作,因为这类修复通常需要修改 HDFS 上的文件,有时甚至需要人工介入。对于这类高危修复操作,建议先执行 hbck-details ,然后根据结果详细了解问题细节,再执行相应的修复操作。

笔者建议在 overlap 分析的基础上使用 merge 命令,强制将存在 overlap 的相关Region 合并到一起。需要注意的是,对于多个Region,需要两两合并,之后对合并后的Region 执行 Major Compaction ,再两两合并。

这里需要特别注意: -repair|-fix 命令强烈不建议生产线上使用。

使用案例

接下来针对线上出现的一些问题,进行案例分析和提出问题解决的方案。

1. Region 没有部署到任何 RegionServer 节点 

执行 hbck 命令后,确认 Region 元数据信息在 HDFS 和 hbase:meta 中都存在,但没有部署到任何一台 RegionServer 上,hbck 命令的输出如下:

  1. ERROR:Region {meta => NEW_REGION_XXXXXXXXXX,\x001\x01,1566748778085.652f5c6d6623f060adeefc622d8ffead., hdfs => hdfs://xxxxx/default/NEW_REGION_XXXXXXXXXX/652f5c6d6623f060adeefc622d8ffead, deploy => } not deployed on any region server.

  2. ......

  3. ERROR: There is a hole in the region chain between and . You need to create a new .regioninfo and region dir in hdfs to plug the hole.

  4. ERROR: Found inconsistency in table NEW_REGION_XXXXXXXXXX

如果在 hbck 命令输出的详细的信息中看到 “not deployed on any region server”,可以使用如下命令修复:

  1. hbase hbck -fixAssignments

当然,我们也可以在 hbase shell 中执行 assign 命令部署指定 Region。

需要特别注意的是,hbck命令输出中如果包含 “There is a hole in the region chain...“ 这样的信息,暂时不用处理,先执行 -fixAssignments 命令,再执行 hbck 命令,看看是否还会输出这样的信息。

2. Region 没有部署到任何 RegionServer 节点且元数据表中对应记录为空 

执行 hbck 命令检查之后,发现 Region 元数据信息只在 HDFS 中存在,在 hbase:meta 中不存在,而且没有部署到任何一台 RegionServer 节点上,hbck 命令输出如下:

  1. ERROR:Region {meta => null, hdfs => hdfs://xxxxx/default/NEW_REGION_XXXXXXXXXX/652f5c6d6623f060adeefc622d8ffead, deploy => } ON HDFS, but not listed in hbase:meta or deployed on any region server.

  2. ......

  3. ERROR: There is a hole in the region chain between and . You need to create a new .regioninfo and region dir in hdfs to plug the hole.

  4. ERROR: Found inconsistency in table NEW_REGION_XXXXXXXXXX

可以看到错误信息 “ON HDFS, but not listed in hbase:meta or deployed on any region server”,可以使用如下命令修复:

  1. hbase hbck -fixMeta -fixAssignments

hbck 命令输出中如果包含 “There is a hole in the region chain...“ 这样的信息,暂时不用处理,先执行 -fixMeta-fixAssignments 命令,再执行 hbck 命令,看看是否还会输出这样的信息。

3. HBase version file 丢失 

HBase 集群启动时会加载 HDFS 上的 version file(/hbase/hbase.version) 来确认HBase 的版本信息,如果该文件丢失或者损坏,则系统无法正常启动。此时可以使用如下命令修复:

  1. hbase hbck -fixVersionFile

该命令重新生成一份 hbase.version 文件,文件中 HBase 集群版本信息来自当前运行的HBCK版本。

生产环境的实践总结:

  • 周期性地多次运行 hbck 命令,检查是否有异常信息

  • 对表单独 hbck 修复,不要对整个集群修复

  • 对于 overlap 的情况,需要管理员认真分析,再谨慎使用 hbck 命令修复。建议最大可能手工修复。

  • 如果 hbck 工具无法修复集群的不一致,需要结合日志进行进一步分析,决定修复方案。

HBase核心参数配置

Region

hbase.hregion.max.filesize : 

默认为 10G,简单理解为 Region 中任意 hstore 所有文件大小的总和大于该值就会进行 split 。

实际生产环境中该值不建议太大,也不能太小。太大会导致系统后台执行 compaction 消耗大量系统资源,一定程度上影响业务响应;太小会导致 Region 分裂比较频繁(分裂本身其实对业务读写会有一定影响),另外单个 RegionServer 中必然存在大量 Region,太多 Region 会消耗大量维护资源,并且在 RegionSever 下线迁移时比较耗时耗资源。

综合考虑,建议线上设置为 50G~80G 左右。

BlockCache

不同 BlockCache 策略对应不同的参数,而且这里参数配置会影响到 Memstore 相关参数的配置。笔者对 BlockCache 策略一直持有这样的观点:RegionServer 内存在20G以内的就选择 LRUBlockCache,大于20G的就选择BucketCache 中的 Offheap 模式。接下来所有的相关配置都基于 BucketCache 的 offheap 模型进行说明。

hfile.block.cache.size : 

默认0.4,该值用来设置LRUBlockCache的内存大小,0.4表示JVM内存的40%。

当前 HBase 系统默认采用 LRUBlockCache 策略,BlockCache 大小和 Memstore 大小均为 JVM 的40%。但对于 BucketCache 策略来讲,Cache 分为了两层,L1 采用LRUBlockCache,主要存储 HFile 中的元数据 Block,L2 采用 BucketCache,主要存储业务数据 Block。因为只用来存储元数据 Block,所以只需要设置很小的 Cache 即可。建议线上设置为 0.05~0.1 左右。

hbase.bucketcache.ioengine : 

BucketCache 策略的模式选择,可选择 heap、offheap 以及 file 三种,分别表示使用堆内内存、堆外内存以及 SSD 硬盘作为缓存存储介质。

hbase.bucketcache.size : 

堆外存大小,设置的大小主要依赖物理内存大小。

配置file模式的参数(线上环境针对内存资源不足,某段时间使用 SSD 存储 cache):

  1. # SSD

  2. hbase.bucketcache.ioengine=file:/disk/disk11/hbase_ssd/cache.data

  3. hbase.bucketcache.size=20GB

Memstore

hbase.hregion.memstore.flush.size : 

默认 128M(134217728),memstore 大于该阈值就会触发 flush。如果当前系统 flush 比较频繁,并且内存资源比较充足,可以适当将该值调整为 256M。调大的副作用可能是造成宕机时需要分裂的 hlog 数量变多,从而延长故障恢复时间。


hbase.hregion.memstore.block.multiplier : 

默认值为 4,表示一旦某 region 中所有写入 memstore 的数据大小总和达到或超过阈值 hbase.hregion.memstore.block.multiplier*hbase.hregion.memstore.flush.size,就会执行 flush 操作,并抛出 RegionTooBusyException 异常。

该值在 HBase 目前版本中默认值为 4,大家可以检查一下 HBase 版本的配置值,记得旧的版本默认值为 2,比较小,需要调整。如果日志中出现如下内容:

  1. RegionTooBusyException: Above memstore limit, regionName=xxxxx ...

这个是 Region 的 memstore 占用内存大小超过正常的 4 倍,这时候会抛异常,写入请求会被拒绝,客户端开始重试请求。当达到 128MB 的时候会触发 flush memstore,当达到128M * 4 还没法触发 flush 时候会抛异常来拒绝写入。

上面的情况,就需要考虑修改该参数值了。


hbase.regionserver.global.memstore.size : 

默认值为 0.4,表示占用总 JVM 内存大小的 40%,该参数非常重要,整个 RegionServer 上所有写入 memstore 的数据大小总和不能超过该阈值,否则会阻塞所有写入请求并强制执行 flush 操作,直至总 memstore 数据大小降到hbase.regionserver.global.memstore.lowerLimit 以下。

该值在 offheap 模式下需要配置为 0.6~0.65。一旦写入出现阻塞,立马查看日志定位,错误信息类似如下:

  1. regionserver.MemStoreFlusher: Blocking updates on hostname,16020,1522286703886: the global memstore size 1.3 G is >= than blocking 1.3 G size

  2. regionserver.MemStoreFlusher: Memstore is above high water mark and block 528ms

一般情况下不会出现这类异常,如果出现需要明确是不是 region 数目太多、单表列族设计太多或者该参数设置太小。


hbase.regionserver.global.memstore.lowerLimit : 

默认值 0.95,表示 RegionServer 级别总 MemStore 大小的低水位是 hbase.regionserver.global.memstore.size 的 95%。这个参数表示 RegionServer 上所有写入 MemStore 的数据大小总和一旦超过这个阈值,就会挑选最大的 MemStore 执行强制flush操作。


hbase.regionserver.optionalcacheflushinterval : 

默认值为 3600000(即 1 小时),hbase 会起一个线程定期 flush 所有 memstore,时间间隔就是该值配置。

生产线上该值建议设大,比如 10h。因为很多场景下1小时 flush 一次会导致产生很多小文件,一方面导致 flush 比较频繁,一方面导致小文件很多,影响随机读性能。因此建议设置较大值。

Compaction

compaction 模块主要用来合并小文件,删除过期数据、delete 数据等。涉及参数较多,对于系统读写性能影响也很重要,下面主要介绍部分比较核心的参数。


hbase.hstore.compactionThreshold : 

默认值为 3,compaction 的触发条件之一,当 store 中文件数超过该阈值就会触发compaction。通常建议生产线上写入较高的系统调高该值,比如 5~10 之间。

如在任意一个 hstore 中有超过此数量的 HStoreFiles,则将运行压缩以将所有 HStoreFiles 文件作为一个 HStoreFile 重新写入。(每次 memstore 刷新写入一个 HStoreFile)您可通过指定更大数量延长压缩,但压缩将运行更长时间。在压缩期间,更新无法刷新到磁盘。长时间压缩需要足够的内存,以在压缩的持续时间内记录所有更新。如太大,压缩期间客户端会超时。


hbase.hstore.compaction.max : 

默认值为 10,最多可以参与 minor compaction 的文件数。该值通常设置为 hbase.hstore.compactionThreshold 的 2~3 倍。


hbase.regionserver.thread.compaction.throttle : 

默认值为 2G,评估单个 compaction 为 small 或者 large 的判断依据。为了防止 large compaction 长时间执行阻塞其他 small compaction,HBase 将这两种 compaction 进行了分离处理,每种 compaction 会分配独立的线程池。


hbase.regionserver.thread.compaction.large/small : 

默认值为 1,large 和 small compaction 的处理线程数。生产线上建议设置为 5,强烈不建议再调太大(比如10),否则会出现性能下降问题。

在写入负载比较高的集群,可以适当增加这两个参数的值,提高系统 Compaction 的效率。但是这两个参数不能太大,否则有可能出现 Compaction 效率不增反降到现象,要结合生产环境测试。


hbase.hstore.blockingStoreFiles :

默认值为10,表示一旦某个 store 中文件数大于该阈值,就会导致所有更新阻塞。生产线上建议设置该值为 100,避免出现阻塞更新,一旦发现日志中出现如下日志信息:

  1. too many store files; delaying flush up to 90000ms

这时就要查看该值是否设置正确了。


hbase.hregion.majorcompaction : 

默认值为 604800000(即 一周),表示 major compaction 的触发周期。

生产线上建议大表手动执行 major compaction,需要将此参数设置为0,即关闭自动触发机制。

HLog

hbase.regionserver.maxlogs : 

默认值为 32,region flush 的触发条件之一,WAL 日志文件总数超过该阈值就会强制执行 flush 操作。该默认值对于很多集群来说太小,生产线上具体设置参考HBASE-14951

可以参考如下:

  1. heap memstore perc maxLogs

  2. 1G 40% 32

  3. 2G 40% 32

  4. 10G 40% 80

  5. 20G 40% 160

  6. 32G 40% 256


hbase.regionserver.hlog.splitlog.writer.threads : 

默认值为 3,regionserver 恢复数据时日志按照 region 切分之后写入 buffer,重新写入HDFS 的线程数。生产环境因为 region 个数普遍较多,为了加速数据恢复,建议设置稍微大点,比如 10。

请求队列相关参数

hbase.regionserver.handler.count : 

默认值为 30,服务器端用来处理用户请求的线程数。生产线上通常需要将该值调到 100~200。

用户关心的请求响应时间由两部分构成:排队时间和服务时间,即 response time = queue time + service time。优化系统需要经常关注排队时间,如果用户请求排队时间很长,首要关注的问题就是 hbase.regionserver.handler.count 是否没有调整。


hbase.ipc.server.callqueue.handler.factor : 

默认值为 0,服务器端设置队列个数,假如该值为 0.1,那么服务器就会设置 handler.count*0.1=30*0.1=3 个队列。


hbase.ipc.server.callqueue.read.ratio : 

默认值为 0,服务器端设置读写业务分别占用的队列百分比以及 handler 百分比。假如该值为0.5,表示读写各占一半队列,同时各占一半 handler。


hbase.ipc.server.call.queue.scan.ratio : 

默认值为 0,服务器端为了将 get 和 scan 隔离需要设置该参数。

队列相关参数设置比较复杂,为了方便理解,举例如下:

  1. <property>

  2. <name>hbase.regionserver.handler.count</name>

  3. <value>100</value>

  4. </property>

  5. <!--RegionServer会设置100*0.1=10个队列处理用户的请求-->

  6. <property>

  7. <name>hbase.ipc.server.callqueue.handler.factor</name>

  8. <value>0.1</value>

  9. </property>

  10. <!--10个队列中分配5(10*0.5)个队列处理用户的读请求,另外5个队列处理用户的写请求-->

  11. <property>

  12. <name>hbase.ipc.server.callqueue.read.ratio</name>

  13. <value>0.5</value>

  14. </property>

  15. <!--5个队列中分配1(5*0.2)个队列处理用户的scan请求,另外4个队列处理用户的get请求-->

  16. <property>

  17. <name>hbaes.ipc.server.callqueue.scan.ratio</name>

  18. <value>0.2</value>

  19. </property>

其他重要参数

hbase.online.schema.update.enable : 

默认值为 true,表示更新表 schema 的时候不再需要先 disable 再 enable,直接在线更新即可。该参数在HBase 2.0之后默认为true。

生产线上建议设置为true。


hbase.quota.enabled : 

默认值为 false,表示是否开启 quota 功能,quota 的功能主要是限制用户/表的 QPS,起到限流作用。


hbase.snapshot.enabled : 

默认值为 true,表示是否开启 snapshot 功能,snapshot 功能主要用来备份 HBase 数据。

生产线上建议设置为true。


zookeeper.session.timeout : 

默认值为 180s,表示 Zookeeper 客户端与服务器端 session 超时时间,超时之后 RegionServer 将会被踢出集群。

有两点需要重点关注:其一是该值需要与 Zookeeper 服务器端 session 相关参数一同设置才会生效。但是如果一味的将该值增大而不修改 Zookeeper 服务端参数,可能并不会实际生效。其二是通常情况下离线集群可以将该值设置较大,在线业务需要根据业务对延迟的容忍度考虑设置。


hbase.zookeeper.useMulti : 

默认值为 true,表示是否开启 ZooKeeper 的 multi-update 功能,该功能在某些场景下可以加速批量请求完成,而且可以有效防止部分异常问题。

生产线上建议设置为 true。主要设置为 true 的前提是 ZooKeeper 服务端的版本为3.4以上,否则会出现 ZooKeeper 客户端夯住的情况。


hbase.coprocessor.master.classes : 

生产线上建议设置 org.apache.hadoop.hbase.security.access.AccessController,可以使用 grant 命令对 namespace、table、cf 设置访问权限。


hbase.coprocessor.region.classes : 

生产线上建议设置为 org.apache.hadoop.hbase.security.token.TokenProvider,org.apache.hadoop.hbase.security.access.AccessController,含义如上。

说明

上面只是核心参数的一部分,其他的参数,比如 Kerberos、Replication、Rest、Thrift 和 HDFS Client 等相关模块等参数没有涉及,请参阅资料。

另外,在 HBase 1.0 版本之后,开始支持在线配置更新,可以在不重启 Region Server 的情况下更改部分配置。

HBase 表设计

表名

命名空间加表名,不同业务使用不同的命名空间。

列簇属性设置

  • VERSIONS

    系统保留的最大版本数,默认为 1。

  • BLOCKCACHE

    是否开启 BlockCache,默认为 true。如果该值为 true,数据 Block 从 HFile 加载出来后会被放入读缓存,提供读请求读取。

  • BLOOMFILTER

    布隆过滤器类型,可选项为 NONE、ROW 和 ROWCOL 。默认为 ROW。布隆过滤器可以用于检索一个元素是否在一个集合中,提高随机读的效率。ROWCOL 模式只对指定列的随机读有优化作用,如果只是根据 rowkey 查询,没有指定列,ROWCOL 不起作用。通常使用 ROW 模式。

  • TTL(Time To Live)

    数据失效时间。HBase TTL 过期的数据是通过 Compaction 机制进行删除的。

  • COMPRESSION

    压缩算法,可选项为 LZO、GZ、NONE、SNAPPY 和 LZ4。理论上,SNAPPY 算法的压缩率可以达到 5:1 甚至更高。

    从理论上讲,数据压缩不是 KV 级别的,而是文件 Block 级别的操作。HBase 在写入 Block 到 HDFS 之前首先对数据块进行压缩,再写入底层存储。读数据如果需要涉及到读取 HDFS 文件时,则首先从 HDFS 中加载出 Block 之后进行解压缩,然后缓存到 BlockCache,最后返回给使用的用户。经过测试,生产线上一般推荐使用 SNAPPY。

  • DATA_BLOCK_ENCODING

    数据编码算法,可选项 NONE、PREFIX、DIFF、FAST_DIFF 和 ROW_INDEX_V1。和压缩一样,编码最直接、最重要的作用也是减少数据存储成本,但是消耗大量CPU资源。

    如果使用 HBase 1.x 版本,还有 PREFIX_TREE 编码,但是此编码存在很多问题,比如会引起 Compaction 一直卡住(HBASE-12959),此外还可能造成 Scan miss(HBASE-12817)等原因,不建议生产使用。

    HBase 2.0 版本开始,PREFIX_TREE 已经被移除了。

  • BLOCKSIZE

    HBase 数据块大小。Block 是 HBase 系统文件层写入、读取的最小粒度,默认块大小为 64K。

    对于不同的业务数据,块大小的合理设置对读写性能有很大的影响。通常来说,如果业务请求以 get 请求为主,可以考虑将块设置较小;如果以 scan 请求为主,可以将块调大;默认的 64K 块大小是在 scan 和 get 之间取得的一个平衡。

    调整时,一定要进行充分测试,再决定是否部署生产。

  • DFS_REPLICATION

    数据 Block 在 HDFS 上存储的副本数,默认为 HDFS 系统设置的值(dfs.replication)。

  • IN_MEMORY

    如果表中某些列的数据量不大,但是进行 get 和 scan 操作的频率又特别高,同时业务要求延时低,此时可以采用 IN_MEMORY 效果比较好。

表属性设置

1. 预分区设置属性

不经过预分区设置的业务通常在后期出现数据分布不均衡的情况,造成读写请求不均衡,严重时出现写入阻塞,读取延迟不可控,甚至影响整个集群其他业务。因此,建议所有业务表上线必须做预分区处理。

与预分区相关的配置项主要有 NUMREGIONS 和 SPLITALGO 。NUMREGIONS 表示预分区个数,该属性设置由业务表预估数据量、规划 Region 大小等因素共同决定。

SPLITALGO 表示切分策略,可选项有 UniformSpilt 和 HexStringSplit 两种:

  • HexStringSplit 策略 适用于 rowkey 前缀是十六进制字符串的场景,比如经过 MD5 编码为十六进制的 rowkey;

  • UniformSpilt 策略 适用于 rowkey 是比较随机的字符数组的场景,比如经过某些 hash 算法转换为字节数组的 rowkey。

当然,用户也可以通过实现 org.apache.hadoop.hbase.util.RegionSplitter.SplitAlgorithm 接口定制实现自己业务表的切分策略。

使用示例:

  1. # create table with four regions based on random bytes keys

  2. hbase>create 't2','f1', { NUMREGIONS => 4 , SPLITALGO => 'UniformSplit' }


  3. # create table with five regions based on hex keys

  4. hbase>create 't3','f1', { NUMREGIONS => 5, SPLITALGO => 'HexStringSplit'

当然也可以通过 Ruby 创建:

  1. # generate splits for long (Ruby fixnum) key range from start to end key

  2. hbase(main):070:0> def gen_splits(start_key,end_key,num_regions)

  3. hbase(main):071:1> results=[]

  4. hbase(main):072:1> range=end_key-start_key

  5. hbase(main):073:1> incr=(range/num_regions).floor

  6. hbase(main):074:1> for i in 1 .. num_regions-1

  7. hbase(main):075:2> results.push([i*incr+start_key].pack("N"))

  8. hbase(main):076:2> end

  9. hbase(main):077:1> return results

  10. hbase(main):078:1> end

  11. hbase(main):079:0>

  12. hbase(main):080:0> splits=gen_splits(1,2000000,10)

  13. => ["\000\003\r@", "\000\006\032\177", "\000\t'\276", "\000\f4\375", "\000\017B<", "\000\022O{", "\000\025\\\272", "\000\030i\371", "\000\ew8"]

  14. hbase(main):081:0> create 'test_splits','f',SPLITS=>splits

  15. 0 row(s) in 0.2670 seconds


  16. => Hbase::Table - test_splits


2. MAX_FILESIZE 

最大文件大小,功能与配置文件中 hbase.hregion.max.filesize 的配置项相同,默认为10G。

Region 中最大的 Store 所有文件大小总和一旦大于该值,整个 Region 就会执行分裂。

综合考虑,建议线上设置为5G-30G。

3. READONLY 

只读表,默认为 false。


4. COMPACTION_ENABLED

Compaction 是否开启,默认为 true,表示允许 Minor/Major Compaction 自动执行。


5. MEMSTORE_FLUSHSIZE 

单个 MemStore 的大小,功能与配置文件中 hbase.hregion.memstore.flush.sizse 的配置项相同,默认为 128M。

如果要求写入吞吐量非常大,可以将该值设置较大。


6. DURABLITY 

WAL 持久化等级,默认为 SYNC_WAL。其他可选项有 SKIP_WAL、ASYNC_WAL 以及 FSYNC_WAL。根据业务的重要程度以及写入吞吐量的要求综合考虑该配置项的设置,比如部分业务写入吞吐量非常大,但数据并不是很重要,允许在异常情况下丢失部分数据,则可以选择 SKIP_WAL 或者 ASYNC_WAL。

将一些参数提升到表级别,甚至列簇级别,非常方便业务定制化,不需要重启集群。

Salted Table

如果 rowkey 本身不具备前缀散列性,例如时间戳,如果把时间戳作为 rowkey,连续的时间戳会存在同一个 Region,造成数据热点问题。

因此对 rowkey 做哈希就是一种很好的解决数据热点的方式。

例如,业务要存放的 rowkey = row1,通过哈希算法算出来一个前缀: PREFIX=MD5SUM(rowkey)%4,实际在 HBase 中存放的的 rowkey=PREFIX+row1

在创建表的时候,把表预分区为 5 个 Regions:

  • region-00 对应区间 (-oo,01)

  • region-01 对应区间 [01,02)

  • region-02 对应区间 [02,03)

  • region-03 对应区间 [03,+oo)

这样,很好地保证了每一个 rowkey 都随机、均匀地落在不同的 region 上,解决了数据热点的问题。在 get 操作的时候,假设待读取的 rowkey=rowl,则先算出 HBase 中实际存储的 rowkey=MD5SUM(row1)%4+row1,按照这个 rowkey 去 get 数据即可。

但是,对于 Salted Table 的 scan 操作,就会稍微麻烦一点,会转换为多个 Region 的多路归并算法(HBase 的 Release 中未实现,可以借助 Phoenix),性能上比正常要差不少。

HBase-HDFS 调优策略

1. Short-Circuit Local Read

当前 HDFS 读取数据都需要经过 DataNode,客户端会向 DataNode 发送读取数据的请求,DataNode 接收到请求后从磁盘中将数据读取出来,再通过 TCP 发送给客户端。

对于本地数据,Short Circuit Local Read 策略允许客户端绕过 HDFS DataNode 直接从磁盘读取本地数据,减少多次网络传输开销,读取效率更高。

开启 Short Circuit Local Read,配置参数:

  1. <configuration>

  2. <property>

  3. <name>dfs.client.read.shortcircuit</name>

  4. <value>true</value>

  5. </property>

  6. <property>

  7. <name>dfs.domain.socket.path</name>

  8. <value>/var/lib/hadoop-hdfs/dn_socket</value>

  9. </property>

  10. <property>

  11. <name>dfs.client.read.shortcircuit.buffer.size</name>

  12. <value>131072</value>

  13. </property>

  14. </configuration>

需要注意的是 dfs.client.read.shortcircuit.buffer.size 参数默认值为 1M,对于 HBase 系统来说有可能造成 OOM(HBASE-8143)。

2. Hedged Read

HBase 数据在 HDFS 中默认存储 3 个副本,通常情况下,HBase 会根据一定算法优先选择一个 DataNode 进行数据读取。根据 Hedged Read 策略,如果在指定时间内读取请求没有返回,HDFS 客户端就会向第二个副本发送第二次数据请求,并且谁先返回就使用谁,之后返回的将会被丢弃。

开启 Hedged Read 功能,设置:

  1. <configuration>

  2. <property>

  3. <name>dfs.client.hedged.read.threadpool.size</name>

  4. <value>20</value> <!--20 threads-->

  5. </property>

  6. <property>

  7. <name>dfs.client.hedged.read.threshold.millis</name>

  8. <value>10</value> <!--10 milliseconds-->

  9. </property>

  10. </configuration>

其中,参数 dfs.client.hedged.read.threadpool.size 表示用于 hedged read 的线程池线程数量,默认为 0,表示关闭 hedged read 功能。参数 dfs.client.hedged.read.threshold.millis 表示 HDFS 数据读取超时时间,超过这个阈值,HDFS客户端将会再发起一次读取请求。

3. Region Data Locaity

Region Data Locaity,即数据本地率,表示当前 Region 的数据在 Region 所在节点存储的比例。提高本地化率,可以有效优化随机读性能。

数据本地化率低通常是由于 Region 迁移(自动 balance 开启、RegionServer 宕机迁移、手工迁移等)导致的,因此可以通过避免 Region 无故迁移来维护数据高本地率,具体措施有关闭自动balance,RegionServer 宕机及时拉起并迁回迁移走的 Region 等。另外,如果数据本地化率很低,还可以在业务低峰期通过执行 major_compact 将数据本地化率提升到 100%。

执行 major_compact 提升数据本地率的理论依据是,major_compact 本质上是将 Region 中的所有文件读取出来然后写入一个大文件,写大文件必然会在本地 DataNode 生成一个副本。

HBase 读取性能优化

HBase服务器优化

1. 读请求是否均衡

Rowkey 必须进行散列化处理,同时建表必须进行预分区处理。

2. BlockCache 设置是否合理

如果 JVM 内存配置量小于 20G,BlockCache 策略选择 LRUBlockCache;否则选择 BucketCache 策略的 offheap 模式。

3. HFile 文件是否太多

一个 Store 中包含多个 HFile 文件,文件越多,检索所需的 IO 次数越多,读取延迟也越高。文件数量通常取决于 Compaction 的执行策略,一般和两个配置参数有关: hbase.hstore.compactionThreshold 和 hbase.hstore.compaction.max.size ,前者表示一个 store 中的文件数超过阈值就应该进行合并,后者表示参与合并的文件大小最大是多少,超过此大小的文件不能参与合并。

可以查看 RegionServer 级别以及 Region 级别的HFile数量,确认 HFile 文件是否过多。

hbase.hstore.compactionThreshold 设置不能太大,默认为3个。

4. Compaction 是否消费系统资源过多

对于大 Region 读延迟敏感的业务(100G以上)通常不建议开启自动 Major Compaction,手动低峰期触发。小 Region 或者延迟不敏感的业务可以开启 Major Compaction,但建议限制流量。

5. 数据本地率是不是很低

尽量避免 Region 无故迁移。对于本地化率较低的节点,可以在业务低峰期执行 major_compact。

HBase客户端优化

1. scan 缓存是否设置合理

大 scan 场景下将 scan 缓存从 100 增大到 500 或者 1000,用以减少 RPC 次数。

2. get 是否使用批量请求

HBase 分别提供了单条 get 以及批量 get 的 API 接口,使用批量 get 接口可以减少客户端到 RegionServer 之间的 RPC 连接数,提高读取吞吐量。

3. 请求是否可以显式指定列簇或者列

尽量指定列簇或者列进行精确查询。

4. 离线批量读取请求是否设置禁止缓存

离线批量读取请求设置禁用缓存,scan.setCacheBlocks(false)

HBase列簇设计优化

建议,任何业务都应该设置布隆过滤器,通常设置为 row,除非确定业务随机查询类型为 row + column,则设置为 rowcol。

HBase 写入性能调优

HBase 服务器端优化

1. Region 是否太少

在表的 Region 数量小于 RegionServer 节点数的场景下,需要将切分部分请求负载高的Region,并迁移到其他 RegionServer 节点上。

2. 写入请求是否均衡

检查 RowKey 设计以及预分区策略,保证写入请求均衡。

3. 使用 SSD 存储 WAL

将 WAL 文件写到SSD上,对于写性能会有非常大的提升。

使用该特性配置步骤:

  • 使用 HDFS Archival Storage 机制,配置 HDFS 的部分文件目录为 SSD 介质

  • hbase-site.xml 添加配置

  1. <property>

  2. <name>hbase.wal.storage.policy</name>

  3. <value>ONE_SSD</value>

  4. </property>

hbase.wal.storage.policy 默认为 none,用户可以指定 ONESSD(WAL 一个副本写入 SSD 介质)或者 ALLSSD(WAL的三个副本全部写入 SSD 介质)。

HBase客户端优化

1. 是否可以使用 Bulkload 方案写入

Bulkload 是一个 MapReduce 程序,输出 HFile 文件。

2. 是否需要写WAL?WAL是否需要同步写入

数据写入流程可以理解为 一次顺序写WAL+一次写缓存

WAL 的持久化分为四个等级:

  • SKIP_WAL

  • ASYNC_WAL

  • SYNC_WAL

  • FSYNC_WAL

默认为 SYNC_WAL 等级持久化数据。

根据业务关注点在 WAL 机制和写入吞吐量之间作出选择,用户可以通过客户端设置 WAL 持久化策略。

3. Put 是否可以同步批量提交

类似 Get 接口。

4. Put 是否可以异步批量提交

开启异步批量提交,用户可以设置 setAutoFlush(false),客户端缓存达到阈值(默认2M)后批量提交给 RegionServer。如果客户端异常,缓存数据可能丢失。

5. 写入 KeyValue 数据是否太大

KeyValue 大小对写入性能影响巨大。

参考

  • 《HBase原理与实践》

  • http://hbase.apache.org/book.html



 猜你喜欢:

我用 Python 集齐了五福
全面解读Hash Join(面试系列)
余额宝背后的中台架构及落地实践!
基于 Flink 构建 CEP 实践
B站2019最美夜弹幕高频词居然是它!
罕见罚单:一农商行因"数据治理"被罚
小米流式平台|实时数仓架构演进与实践
HBase调优及优化的10种方式
数据同步之道(加薪系列)
基于Flink SQL构建实时数据仓库
菜鸟数据中台技术演进之路





数仓社区

如有收获,请划至底部,点击“在看”,谢谢!


资源下载

关注公众号:数据仓库与Python大数据 回复关键字获取哦

06,数仓经典书籍

07,  python基础入门

中台,中台 PPT

体系,OneData体系PPT

实时数仓FFA 实时数仓视频回顾

Kettle,Kettle视频

Kylin,Kylin视频

Flink,Flink资料

Python,零基础学Python教程视频


加群添加iom1128 备注:数据,拉你入群

升职加薪

关注我们,获取更多 技术干货与福利哦

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存